// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *******************************************************************************
 * Copyright (C) 1997-2007, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */

package com.ibm.icu.dev.demo.calendar;

import com.ibm.icu.dev.demo.impl.DemoApplet;
import com.ibm.icu.dev.demo.impl.DemoUtility;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.util.BuddhistCalendar;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.JapaneseCalendar;
import com.ibm.icu.util.SimpleTimeZone;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
import java.util.Locale;

/**
 * A Frame is a top-level window with a title. The default layout for a frame is BorderLayout. The
 * CalendarFrame class defines the window layout of CalendarDemo.
 */
class CalendarFrame extends Frame {
    /** For serialization */
    private static final long serialVersionUID = -4289697663503820619L;

    private static final boolean DEBUG = false;

    private DemoApplet applet;

    /** Constructs a new CalendarFrame that is initially invisible. */
    public CalendarFrame(DemoApplet myApplet) {
        super("Calendar Demo");
        this.applet = myApplet;
        init();

        // When the window is closed, we want to shut down the applet or application
        addWindowListener(
                new WindowAdapter() {
                    public void windowClosing(WindowEvent e) {
                        setVisible(false);
                        dispose();

                        if (applet != null) {
                            applet.demoClosed();
                        } else System.exit(0);
                    }
                });
    }

    private Choice displayMenu;
    private Locale[] locales = DemoUtility.getG7Locales();

    private Calendar calendars[] = new Calendar[2];
    private Choice calMenu[] = new Choice[2];
    private ColoredLabel monthLabel[] = new ColoredLabel[2];
    private DateFormat monthFormat[] = new DateFormat[2];

    private Button prevYear;
    private Button prevMonth;
    private Button gotoToday;
    private Button nextMonth;
    private Button nextYear;
    private CalendarPanel calendarPanel;

    private static void add(
            Container container,
            Component component,
            GridBagLayout g,
            GridBagConstraints c,
            int gridwidth,
            int weightx) {
        c.gridwidth = gridwidth;
        c.weightx = weightx;
        g.setConstraints(component, c);
        container.add(component);
    }

    /**
     * Initializes the applet. You never need to call this directly, it is called automatically by
     * the system once the applet is created.
     */
    public void init() {
        setBackground(DemoUtility.bgColor);
        setLayout(new BorderLayout(10, 10));

        Panel topPanel = new Panel();
        GridBagLayout g = new GridBagLayout();
        topPanel.setLayout(g);
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;

        // Build the two menus for selecting which calendar is displayed,
        // plus the month/year label for each calendar
        for (int i = 0; i < 2; i++) {
            calMenu[i] = new Choice();
            for (int j = 0; j < CALENDARS.length; j++) {
                calMenu[i].addItem(CALENDARS[j].name);
            }
            calMenu[i].setBackground(DemoUtility.choiceColor);
            calMenu[i].select(i);
            calMenu[i].addItemListener(new CalMenuListener());

            // Label for the current month name
            monthLabel[i] = new ColoredLabel("", COLORS[i]);
            monthLabel[i].setFont(DemoUtility.titleFont);

            // And the default calendar to use for this slot
            calendars[i] = CALENDARS[i].calendar;

            add(topPanel, calMenu[i], g, c, 5, 0);
            add(topPanel, monthLabel[i], g, c, GridBagConstraints.REMAINDER, 1);
        }

        // Now add the next/previous year/month buttons:
        prevYear = new Button("<<");
        prevYear.addActionListener(new AddAction(Calendar.YEAR, -1));

        prevMonth = new Button("<");
        prevMonth.addActionListener(new AddAction(Calendar.MONTH, -1));

        gotoToday = new Button("Today");
        gotoToday.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        calendarPanel.setDate(new Date());
                        updateMonthName();
                    }
                });

        nextMonth = new Button(">");
        nextMonth.addActionListener(new AddAction(Calendar.MONTH, 1));

        nextYear = new Button(">>");
        nextYear.addActionListener(new AddAction(Calendar.YEAR, 1));

        c.fill = GridBagConstraints.NONE;
        add(topPanel, prevYear, g, c, 1, 0);
        add(topPanel, prevMonth, g, c, 1, 0);
        add(topPanel, gotoToday, g, c, 1, 0);
        add(topPanel, nextMonth, g, c, 1, 0);
        add(topPanel, nextYear, g, c, 1, 0);

        // Now add the menu for selecting the display language
        Panel displayPanel = new Panel();
        {
            displayMenu = new Choice();
            Locale defaultLocale = Locale.getDefault();
            int bestMatch = -1, thisMatch = -1;
            int selectMe = 0;

            for (int i = 0; i < locales.length; i++) {
                if (i > 0 && locales[i].getLanguage().equals(locales[i - 1].getLanguage())
                        || i < locales.length - 1
                                && locales[i].getLanguage().equals(locales[i + 1].getLanguage())) {
                    displayMenu.addItem(locales[i].getDisplayName());
                } else {
                    displayMenu.addItem(locales[i].getDisplayLanguage());
                }

                thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale);

                if (thisMatch >= bestMatch) {
                    bestMatch = thisMatch;
                    selectMe = i;
                }
            }

            displayMenu.setBackground(DemoUtility.choiceColor);
            displayMenu.select(selectMe);

            displayMenu.addItemListener(
                    new ItemListener() {
                        public void itemStateChanged(ItemEvent e) {
                            Locale loc = locales[displayMenu.getSelectedIndex()];
                            calendarPanel.setLocale(loc);
                            monthFormat[0] = monthFormat[1] = null;
                            updateMonthName();
                            repaint();
                        }
                    });

            Label l1 = new Label("Display Language:", Label.RIGHT);
            l1.setFont(DemoUtility.labelFont);

            displayPanel.setLayout(new FlowLayout());
            displayPanel.add(l1);
            displayPanel.add(displayMenu);
        }
        c.fill = GridBagConstraints.NONE;
        c.anchor = GridBagConstraints.EAST;

        add(topPanel, displayPanel, g, c, GridBagConstraints.REMAINDER, 0);

        // The title, buttons, etc. go in a panel at the top of the window
        add("North", topPanel);

        // The copyright notice goes at the bottom of the window
        Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
        copyright.setFont(DemoUtility.creditFont);
        add("South", copyright);

        // Now create the big calendar panel and stick it in the middle
        calendarPanel = new CalendarPanel(locales[displayMenu.getSelectedIndex()]);
        add("Center", calendarPanel);

        for (int i = 0; i < 2; i++) {
            calendarPanel.setCalendar(i, calendars[i]);
            calendarPanel.setColor(i, COLORS[i]);
        }

        updateMonthName();
    }

    private void updateMonthName() {
        for (int i = 0; i < 2; i++) {
            try {
                if (monthFormat[i] == null) { // TODO: optimize
                    DateFormat f =
                            DateFormat.getDateTimeInstance(
                                    calendars[i],
                                    DateFormat.MEDIUM,
                                    -1,
                                    locales[displayMenu.getSelectedIndex()]);
                    if (f instanceof com.ibm.icu.text.SimpleDateFormat) {
                        com.ibm.icu.text.SimpleDateFormat f1 =
                                (com.ibm.icu.text.SimpleDateFormat) f;
                        f1.applyPattern("MMMM, yyyy G");
                        f1.setTimeZone(new SimpleTimeZone(0, "UTC"));
                    }
                    monthFormat[i] = f;
                }
            } catch (ClassCastException e) {
                // hey {lw} - there's something wrong in this routine that causes exceptions.
                System.out.println(e);
            }

            monthLabel[i].setText(monthFormat[i].format(calendarPanel.firstOfMonth()));
        }
    }

    /**
     * CalMenuListener responds to events in the two popup menus that select the calendar systems to
     * be used in the display. It figures out which of the two menus the event occurred in and
     * updates the corresponding element of the calendars[] array to match the new selection.
     */
    private class CalMenuListener implements ItemListener {
        @SuppressWarnings("ReferenceEquality")
        public void itemStateChanged(ItemEvent e) {
            for (int i = 0; i < calMenu.length; i++) {
                if (e.getItemSelectable() == calMenu[i]) {
                    // We found the menu that the event happened in.
                    // Figure out which new calendar they selected.
                    Calendar newCal = CALENDARS[calMenu[i].getSelectedIndex()].calendar;
                    if (newCal != calendars[i]) {
                        // If any of the other menus are set to the same new calendar
                        // we're about to use for this menu, set them to the current
                        // calendar from *this* menu so we won't have two the same
                        for (int j = 0; j < calendars.length; j++) {
                            if (j != i && calendars[j] == newCal) {
                                calendars[j] = calendars[i];
                                calendarPanel.setCalendar(j, calendars[j]);
                                monthFormat[j] = null;

                                for (int k = 0; k < CALENDARS.length; k++) {
                                    if (calendars[j] == CALENDARS[k].calendar) {
                                        calMenu[j].select(k);
                                        break;
                                    }
                                }
                            }
                        }
                        // Now update this menu to use the new calendar the user selected
                        calendars[i] = newCal;
                        calendarPanel.setCalendar(i, newCal);
                        monthFormat[i] = null;

                        updateMonthName();
                    }
                    break;
                }
            }
        }
    }

    /** AddAction handles the next/previous year/month buttons... */
    private class AddAction implements ActionListener {
        AddAction(int field, int amount) {
            this.field = field;
            this.amount = amount;
        }

        public void actionPerformed(ActionEvent e) {
            calendarPanel.add(field, amount);
            updateMonthName();
        }

        private int field, amount;
    }

    /**
     * ColoredLabel is similar to java.awt.Label, with two differences:
     *
     * <p>- You can set its text color
     *
     * <p>- It draws text using drawString rather than using a host-specific "Peer" object like AWT
     * does. On 1.2, using drawString gives us Bidi reordering for free.
     */
    private static class ColoredLabel extends Component {
        /** For serialization */
        private static final long serialVersionUID = 5004484960341875722L;

        public ColoredLabel(String label) {
            text = label;
        }

        public ColoredLabel(String label, Color c) {
            text = label;
            color = c;
        }

        public void setText(String label) {
            text = label;
            repaint();
        }

        public void setFont(Font f) {
            font = f;
            repaint();
        }

        public void paint(Graphics g) {
            FontMetrics fm = g.getFontMetrics(font);

            Rectangle bounds = getBounds();

            g.setColor(color);
            g.setFont(font);
            g.drawString(
                    text,
                    fm.stringWidth("\u00a0"),
                    bounds.height / 2 + fm.getHeight() - fm.getAscent() + fm.getLeading() / 2);
        }

        public Dimension getPreferredSize() {
            return getMinimumSize();
        }

        public Dimension getMinimumSize() {
            FontMetrics fm = getFontMetrics(font);

            return new Dimension(
                    fm.stringWidth(text) + 2 * fm.stringWidth("\u00a0"),
                    fm.getHeight() + fm.getLeading() * 2);
        }

        String text;
        Color color = Color.black;
        Font font = DemoUtility.labelFont;
    }

    /** Print out the error message while debugging this program. */
    public void errorText(String s) {
        if (DEBUG) {
            System.out.println(s);
        }
    }

    class CalendarRec {
        public CalendarRec(String nameStr, Calendar cal) {
            name = nameStr;
            calendar = cal;
        }

        Calendar calendar;
        String name;
    }

    private final CalendarRec[] CALENDARS = {
        new CalendarRec("Gregorian Calendar", new GregorianCalendar()),
        new CalendarRec("Hebrew Calendar", new HebrewCalendar()),
        new CalendarRec("Islamic Calendar", makeIslamic(false)),
        new CalendarRec("Islamic Civil Calendar ", makeIslamic(true)),
        new CalendarRec("Buddhist Calendar", new BuddhistCalendar()),
        new CalendarRec("Japanese Calendar", new JapaneseCalendar()),
    };

    private static final Calendar makeIslamic(boolean civil) {
        IslamicCalendar cal = new IslamicCalendar();
        cal.setCivil(civil);
        return cal;
    }

    static final Color[] COLORS = {Color.blue, Color.black};
}
